/******************************************************************************
*
* This file is part of FreeTrack (http://code.google.com/p/qtfreetrack).
* BY Willy Gardiol (willy@gardiol.org)
*
* FreeTrack is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FreeTrack is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar.  If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/

#include "ftcontrols.h"
#include "ftlist.h"

FTList::FTList(QWidget *parent) :
    QWidget(parent)
{
    scrolling = false;
    // This widget will always expand to all the available space
    setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored );

    scroll_start = scroll_offset = 0;
    row_height = FTControls::defaultHeight();
    _selectedIndex = -1;
    keysMap.clear();
    highlighted.clear();
    baseKey = FTL_BASE_KEY;
}

QList<int> FTList::keys() const
{
    // keys: indexes, values = keys
    return keysMap;
}

void FTList::highlightClear()
{
    for (int n = 0; n < highlighted.count(); n++ )
        highlighted[n] = false;
    update();
}

void FTList::highlightKey(int key, bool h)
{
    int index = keyToIndex( key );
    if ( index > -1 )
        highlightId( index, h );
}

void FTList::highlightId(int id, bool h)
{
    if ( (id > -1) && (id < strings.count() ) )
    {
        highlighted[id] = h;
        update();
    }
}

void FTList::mousePressEvent(QMouseEvent * event)
{
    if ( !scrolling )
        scroll_start = event->globalY();
}

void FTList::mouseMoveEvent(QMouseEvent * event )
{
    // IF not scrolling, check threeshold...
    if ( !scrolling )
    {
        // Calculate distance between this new point and the threeshold starting point
        int shaky_distance = abs(event->globalY() - scroll_start);
        // do the state change if the finger moved more than our threeshold
        scrolling = shaky_distance > FTControls::shakyThreeshold();
    }
    else
    {
        scroll_offset += event->globalY() - scroll_start;
        scroll_start = event->globalY();
        update();
    }
}

void FTList::mouseReleaseEvent(QMouseEvent * event)
{
    if ( scrolling )
    {
        scrolling = false;
    }
    else if ( strings.count() > 0 )
    {
        _selectedIndex = (event->y() - scroll_offset)/row_height;
        if ( _selectedIndex <  0 )
            _selectedIndex = 0;
        if ( _selectedIndex >= strings.count() )
            _selectedIndex = strings.count()-1;
        update();
        qApp->processEvents( QEventLoop::AllEvents );
        emit indexSelected( _selectedIndex );
        // keys: indexes, values = keys
        emit keySelected( keysMap.at( _selectedIndex ) );
        emit stringSelected( strings.at( _selectedIndex ) );
    }
}

void FTList::selectIndex(int index)
{
    int ns = strings.count();
    if ( ns < 0 )
        return;

    if ( index < 0 )
        index = 0;
    else if ( index >= ns )
        index = ns-1;

    _selectedIndex = index;
    scrollToFit();
    update();
}

void FTList::selectString(QString s)
{
    if ( strings.contains(s) )
        selectIndex(strings.indexOf(s));
}

void FTList::selectKey(int key)
{
    int index = keyToIndex( key );
    if ( index > -1 )
        selectIndex( index );
}

QString FTList::selectedString() const
{
    return strings.at( _selectedIndex );
}

int FTList::selectedIndex() const
{
    return _selectedIndex;
}

int FTList::selectedKey() const
{
    // keys: indexes, values = keys
    return keysMap.at( _selectedIndex );
}

void FTList::scrollToFit()
{
    scroll_offset = -_selectedIndex * row_height;
}

void FTList::paintEvent(QPaintEvent *)
{
    QPalette palette = QApplication::palette();
    QPainter painter(this);

    palette.setCurrentColorGroup( FT_COLOR_GROUP );
    QColor windowText = palette.color( QPalette::WindowText );
    QColor highlightText = palette.color( QPalette::HighlightedText );
    QBrush windowBrush = palette.brush( QPalette::Window );
    QBrush highlightBrush = palette.brush( QPalette::Highlight );
    QFont normal_font = font();
    QFont highlighted_font = normal_font;
    highlighted_font.setBold(true);

    int widget_width = width()-4;
    int widget_height = height()-4;
    int num_strings = strings.count();

    // Calculate overall height
    int total_height = num_strings*row_height;
    int delta = widget_height - total_height;

    // We have two cases:
    // 1) total_height < height
    if ( total_height <=  widget_height)
    {
        if ( scroll_offset < 0 )
            scroll_offset = 0;
        if ( scroll_offset > delta )
            scroll_offset = delta;
    }
    else
    {
        // 2) total_height > height
        if ( scroll_offset > 0 )
            scroll_offset = 0;
        if ( scroll_offset < delta )
            scroll_offset = delta;
    }

    int start_i = qMax( -scroll_offset/row_height, 0 );
    int last_i = qMin( strings.count(), widget_height/row_height + start_i+2);

    // Define the row rectangle
    QRect row_rect = QRect( 3, scroll_offset+start_i*row_height, widget_width-3, row_height );

    for ( int i = start_i; i < last_i; i++ )
    {
        painter.setPen( windowText );
        // Paint the background...
        // Add a nice "border" effect...
        if (i == _selectedIndex)
        {
            painter.fillRect( row_rect, highlightBrush );
            painter.setPen( highlightText );
        }
        else
            painter.fillRect( row_rect, windowBrush );
        // Draw the string...
        painter.drawRect( row_rect );
        painter.setFont( highlighted.at(i) ? highlighted_font : normal_font );
        painter.drawText( row_rect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, strings.at(i) );
        row_rect.translate(0, row_height);
    }

    painter.setPen( windowText );

    // Draw a border to the widget
    painter.drawRect( rect().adjusted(1,1,-2,-2) );

    if ( total_height > widget_height )
    {
        int prop_h = total_height/widget_height;
        int bar_height = widget_height / prop_h - 14;
        int bar_start = abs(scroll_offset) / prop_h + 5;
        painter.setBrush( QBrush( Qt::Dense4Pattern ) );
        painter.drawRect( widget_width - 7, bar_start, 5, bar_height );
    }
}

void FTList::clear()
{
    keysMap.clear();
    strings.clear();
    highlighted.clear();
    scroll_offset = 0;
    _selectedIndex = -1;
    scrolling = false;
    update();
}

void FTList::appendStrings(const QStringList & sl )
{
    foreach (const QString& str, sl )
        appendString( str, baseKey++ );
}

void FTList::appendString(const QString & s )
{
    appendString( s, baseKey++ );
}

void FTList::appendString(const QString & s, int key )
{
    strings.append( s );
    highlighted.append( false );
    keysMap.append( key );
    update();
}

int FTList::keyToIndex(int key) const
{
    return keysMap.indexOf( key );
}

int FTList::indexToKey(int index) const
{
    return keysMap.at( index );
}

void FTList::updateString(int key, const QString& str)
{
    int index = keyToIndex(key);
    if ( index > -1 )
        updateStringIndex(index, str);
    else
        appendString( str, key );
}

void FTList::deleteString(int key)
{
    int index = keyToIndex(key);
    if ( index > -1 )
        deleteStringIndex(index);
}

void FTList::updateStringIndex(int index, const QString & s)
{
    if ( (index > -1) && ( index < strings.count() ) )
    {
        strings.replace( index, s );
        update();
    }
    else
        appendString( s );
}

void FTList::deleteStringIndex(int index)
{
    if ( (index > -1) && ( index < strings.count() ) )
    {
        strings.takeAt( index );
        highlighted.takeAt( index );
        keysMap.takeAt( index );
        if ( strings.count() == 0 )
            selectIndex(-1);
        else if ( _selectedIndex == index )
            selectIndex( index-1 );
        update();
    }
}
